package com.heima.media.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.dto.ResponseResult;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.minio.MinIOService;
import com.heima.common.util.SensitiveWordUtil;
import com.heima.media.dto.ArticleDto;
import com.heima.media.dto.ContentDto;
import com.heima.media.dto.ImageDto;
import com.heima.media.entity.WmChannel;
import com.heima.media.entity.WmNews;
import com.heima.media.entity.WmSensitive;
import com.heima.media.entity.WmUser;
import com.heima.media.feign.ArticleFeign;
import com.heima.media.service.*;
import org.apache.commons.io.IOUtils;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Service
public class AuditServiceImpl implements IAuditService {

    @Autowired
    private GreenTextScan textScan;

    @Autowired
    private GreenImageScan imageScan;

    @Autowired
    private IWmNewsService newsService;

    @Autowired
    private MinIOService minIOService;

    /**
     * 审核自媒体文章
     *
     * @param wmNews
     */
    @Override
    @Async
    public void audit(WmNews wmNews) {
        System.out.println(Thread.currentThread().getName() + "审核服务开始:" + new Date());

        if (wmNews.getStatus() == 8 || wmNews.getStatus() == 4) {
            if (wmNews.getPublishTime().getTime() > System.currentTimeMillis()) {
                // 修改文章的审核状态为 8 审核通过（待发布）
                publishDelayMsg(wmNews);
            } else {
                // 调用文章微服务接口保存文章
                saveArticle(wmNews);
            }
        }

        // 自媒体文章状态为1 进行自动审核
        if (wmNews.getStatus() == 1) {
            // 获取待检测的文本
            // 检测的内容包含 标题+内容中的文本
            // 提取文本和图片
            Map<String, Object> textAndImageFromNews = getTextAndImageFromNews(wmNews);
            String text = (String) textAndImageFromNews.get("text");
            List<String> image = (List<String>) textAndImageFromNews.get("image");

            // 进行自管理的敏感词审核
            boolean sensitiveResult = sensitiveAudit(text, wmNews);
            if (!sensitiveResult) return;
            // 调用阿里云进行文本审核
            boolean textResult = textAudit(text, wmNews);
            // 获取待检测的图片
            if (!textResult) return;
            // 调用阿里云进行图片审核
            boolean imageResult = imageAudit(image, wmNews);
            if (!imageResult) return;
            // 审核通过
            // 如果文章的发布时间大于当前系统时间,不是立刻发布文章,而是添加延迟任务,等待延迟任务执行才发布文章
            if (wmNews.getPublishTime().getTime() > System.currentTimeMillis()) {
                // 修改文章的审核状态为 8 审核通过（待发布）
                publishDelayMsg(wmNews);
            } else {
                // 调用文章微服务接口保存文章
                saveArticle(wmNews);
            }
            System.out.println(Thread.currentThread().getName() + "审核服务完成:" + new Date());
        }
    }
    /**
     * 发布延迟消息
     * @param wmNews
     */
    private void publishDelayMsg(WmNews wmNews) {
        wmNews.setStatus(8);
        newsService.updateById(wmNews);
        // 发布延迟任务,使用redisson发送延迟消息,指定内容和延迟时间
        // 获取一个阻塞队列,指定队列的名称
        RQueue<String> queue = redissonClient.getBlockingQueue("article-publish");
        // 获取延迟队列
        RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(queue);
        // 计算发布时间到当前时间的差值
        long time = wmNews.getPublishTime().getTime() - System.currentTimeMillis();
        delayedQueue.offer(JSON.toJSONString(wmNews), time, TimeUnit.MILLISECONDS);
    }

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private IWmSensitiveService sensitiveService;

    /**
     * 敏感词审核
     *
     * @param text
     * @param wmNews
     * @return
     */
    private boolean sensitiveAudit(String text, WmNews wmNews) {
        boolean result = false;

        // 初始化敏感词列表
        if (SensitiveWordUtil.dictionaryMap.size() == 0) {
            // List<WmSensitive> list = sensitiveService.list();
            // List<String> words = new ArrayList<>();
            // for (WmSensitive wmSensitive : list) {
            //     words.add(wmSensitive.getSensitives());
            // }

            // select sensitives from wm_sensitive
            LambdaQueryWrapper<WmSensitive> query = new LambdaQueryWrapper<>();
            query.select(WmSensitive::getSensitives);
            List<String> words = sensitiveService.listObjs(query, o -> o.toString());
            SensitiveWordUtil.initMap(words);
        }
        // 审核文章标题
        Map<String, Integer> mapTitle = SensitiveWordUtil.matchWords(wmNews.getTitle());
        if (mapTitle.size() == 0) {
            // 审核文章内容
            Map<String, Integer> mapContent = SensitiveWordUtil.matchWords(text);
            if (mapContent.size() == 0) {
                result = true;
            }
        }
        if (!result) {
            wmNews.setStatus(2);
            wmNews.setReason("敏感词审核不通过");
            newsService.updateById(wmNews);
        }
        return result;
    }

    @Autowired
    private ArticleFeign articleFeign;

    @Autowired
    private IWmUserService userService;

    @Autowired
    private IWmChannelService channelService;

    /**
     * 保存文章
     *
     * @param wmNews
     */
    private void saveArticle(WmNews wmNews) {
        ArticleDto dto = new ArticleDto();
        BeanUtils.copyProperties(wmNews, dto);
        dto.setId(null);
        // 根据自媒体用户表查询作者id
        WmUser wmUser = userService.getById(wmNews.getUserId());
        Integer authorId = wmUser.getApAuthorId();
        dto.setAuthorId(authorId);
        dto.setAuthorName(wmUser.getName());
        // 查询频道信息
        WmChannel wmChannel = channelService.getById(wmNews.getChannelId());
        dto.setChannelName(wmChannel.getName());
        dto.setLayout(wmNews.getType());
        dto.setFlag(0);
        List<ImageDto> images = JSON.parseArray(wmNews.getImages(), ImageDto.class);
        List<String> list = new ArrayList<>();
        for (ImageDto image : images) {
            list.add(image.getUrl());
        }
        String imageStr = String.join(",", list);
        dto.setImages(imageStr);
        dto.setCreatedTime(new Date());
        ResponseResult<Long> responseResult = articleFeign.saveArticle(dto);
        if (responseResult.getCode().equals(AppHttpCodeEnum.SUCCESS.getCode())) {
            Long articleId = responseResult.getData();
            // 将文章的id回写到wmnews中
            wmNews.setArticleId(articleId);
            // 更新自媒体文章状态为9 -- 已发布
            wmNews.setStatus(9);
            newsService.updateById(wmNews);
        }
    }

    /**
     * 阿里云图片审核
     *
     * @param image
     * @param wmNews
     * @return
     */
    private boolean imageAudit(List<String> image, WmNews wmNews) {
        boolean result = false;
        // 判断图片是否为空
        if (image.size() == 0) {
            return true;
        }
        // 调用阿里云图片检测
        // 如果minio搭建在外网,可以直接使用图片地址检测
        // 如果minio搭建在外网,需要先下载图片,再调用阿里云上传图片进行检查
        // 定义需要检查的字节数组集合
        List<byte[]> imageBytes = new ArrayList<>();
        // 先下载图片
        try {
            for (String url : image) {
                InputStream inputStream = minIOService.download(url);
                imageBytes.add(IOUtils.toByteArray(inputStream));
            }
            Map map = imageScan.imageScan(imageBytes);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "pass":
                    result = true;
                    break;
                case "block":
                    // 审核失败  修改自媒体文章状态为2 记录失败的原因
                    wmNews.setStatus(2);
                    String label = (String) map.get("label");
                    wmNews.setReason("阿里云图片审核失败: " + label);
                    newsService.updateById(wmNews);
                    break;
                case "review":
                    // 需要进行人工审核  修改自媒体文章状态为3
                    wmNews.setStatus(3);
                    newsService.updateById(wmNews);
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 阿里云文本审核
     *
     * @param text
     * @return
     */
    private boolean textAudit(String text, WmNews wmNews) {
        boolean result = false;
        try {
            Map map = textScan.greenTextScan(text);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "pass":
                    result = true;
                    break;
                case "block":
                    // 审核失败  修改自媒体文章状态为2 记录失败的原因
                    wmNews.setStatus(2);
                    String label = (String) map.get("label");
                    wmNews.setReason("阿里云文本审核失败: " + label);
                    newsService.updateById(wmNews);
                    break;
                case "review":
                    // 需要进行人工审核  修改自媒体文章状态为3
                    wmNews.setStatus(3);
                    newsService.updateById(wmNews);
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 提取文本内容和图片
     *
     * @param wmNews
     * @return
     */
    private Map<String, Object> getTextAndImageFromNews(WmNews wmNews) {
        Map<String, Object> map = new HashMap<>();
        // 定义文本
        StringBuilder sb = new StringBuilder();
        // 定义图片地址集合
        List<String> images = new ArrayList<>();
        // 添加标题
        sb.append(wmNews.getTitle());
        List<ContentDto> contentDtos = JSON.parseArray(wmNews.getContent(), ContentDto.class);
        for (ContentDto dto : contentDtos) {
            if (dto.getType().equals("text")) {
                // 添加内容中的文本
                sb.append(dto.getValue());
            } else {
                // 添加图片
                images.add(dto.getValue());
            }
        }
        // 图片还包含封面图片,如果是自动类型的话,封面图片是从内容图片中截取的,所以这里要做一个去重判断
        List<ImageDto> coverImages = JSON.parseArray(wmNews.getImages(), ImageDto.class);
        for (ImageDto coverImage : coverImages) {
            if (!images.contains(coverImage.getUrl())) {
                images.add(coverImage.getUrl());
            }
        }
        map.put("text", sb.toString());
        map.put("image", images);
        return map;
    }
}
